package com.ruoyi.odorservice.service.impl;

import com.ruoyi.odorservice.entity.HandleSample;
import com.ruoyi.odorservice.mapper.AnalysisMapper;
import com.ruoyi.odorservice.model.bean.*;
import com.ruoyi.odorservice.model.vo.MaterialTotalTrendVo;
import com.ruoyi.odorservice.model.vo.OdorActivityAnalysisVo;
import com.ruoyi.odorservice.model.vo.OdorousMaterialVo;
import com.ruoyi.odorservice.model.vo.*;
import com.ruoyi.common.core.domain.Result;
import com.ruoyi.odorservice.constant.Template;
import com.ruoyi.odorservice.mapper.MaterialMapper;
import com.ruoyi.odorservice.mapper.SiteMapper;
import com.ruoyi.odorservice.service.AnalysisService;
import lombok.AllArgsConstructor;
import org.apache.commons.math3.stat.correlation.PearsonsCorrelation;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.time.format.DateTimeFormatter;
import java.util.stream.Collectors;
@Service
@AllArgsConstructor
public class AnalysisServiceImpl implements AnalysisService {

    private AnalysisMapper analysisMapper;

    private MaterialMapper materialMapper;

    private SiteMapper siteMapper;

    @Override
    public List<MaterialTotalTrendVo> totalTrend(LocalDateTime start, LocalDateTime end, String siteName) {
        List<MaterialTotalTrendVo> materialTotalTrendVoList = analysisMapper.getTotalTrend(start, end, siteName);
        for(MaterialTotalTrendVo materialTotalTrendVo : materialTotalTrendVoList){
            List<MaterialConcentrationBean> materialConcentrationBeanList = materialTotalTrendVo.getMaterialList();
            double totalConcentration = 0.0;
            if(materialConcentrationBeanList != null && !materialConcentrationBeanList.isEmpty()){
                //如果物质浓度集合大小为1并且这个物质名称是默认值“物质名称”，则代表这个记录没有物质，将物质集合设为空集合，总浓度设为0
                if(materialConcentrationBeanList.size() == 1 && Objects.equals(materialConcentrationBeanList.get(0).getMaterialName(), "物质名称")){
                    materialTotalTrendVo.setMaterialList(new ArrayList<>());
                    materialTotalTrendVo.setTotalConcentration(totalConcentration);
                    continue;
                }
                for (MaterialConcentrationBean materialConcentrationBean : materialConcentrationBeanList) {
                    totalConcentration += (materialConcentrationBean.getConcentration() != null ? materialConcentrationBean.getConcentration() : 0.0);
                }
            }
            materialTotalTrendVo.setTotalConcentration(totalConcentration);
        }
        return materialTotalTrendVoList;
    }

    @Override
    public List<OdorActivityAnalysisVo> odorActivity(LocalDateTime start, LocalDateTime end, String siteName) {
        return analysisMapper.getOdorActivity(start, end, siteName);
    }

    @Override
    public List<CorrelationAnalysisVo> relevanceAnalysis(LocalDateTime start, LocalDateTime end, String siteName) {
        List<CorrelationAnalysisVo> correlationAnalysisVoList = new ArrayList<>();
        //查询气味词及强度值集合
        List<CorrelationAnalysisBean> odorList = analysisMapper.getOdorIntensity(start, end, siteName);
        //查询物质名称及浓度集合
        List<CorrelationAnalysisBean> materialList = analysisMapper.getMaterialConcentration(start, end, siteName);

        if(materialList != null && !materialList.isEmpty()) {
            //删除没有浓度或者是物质名称为默认值的记录
            materialList.removeIf(material -> material.getName().equals("物质名称"));
            for(CorrelationAnalysisBean material : materialList){
                if(odorList != null && !odorList.isEmpty()) {
                    for(CorrelationAnalysisBean odor : odorList){
                        CorrelationAnalysisVo correlationAnalysisVo = new CorrelationAnalysisVo();
                        double[] materialConcentrationArray = material.getValue().stream().mapToDouble(Double::doubleValue).toArray();
                        double[] odorIntensity = odor.getValue().stream().mapToDouble(Double::doubleValue).toArray();
                        double coefficient = getPearsonsCorrelation(odorIntensity, materialConcentrationArray);

                        correlationAnalysisVo.setMaterialName(material.getName());
                        correlationAnalysisVo.setOdorName(odor.getName());
                        correlationAnalysisVo.setCoefficient(coefficient);

                        correlationAnalysisVoList.add(correlationAnalysisVo);
                    }
                }
            }
        }

        return correlationAnalysisVoList;
    }

    private double getPearsonsCorrelation(double[] odorIntensity, double[] materialConcentration) {
        // 确保两个数组的长度相同
        if (odorIntensity.length != materialConcentration.length) {
            //气味强度值和物质浓度值的数组长度不同，无法计算相关性。返回-2
            return -2.0;
        }
        // 创建皮尔逊相关系数计算对象
        PearsonsCorrelation correlation = new PearsonsCorrelation();
        // 计算相关性系数
        double coefficient = correlation.correlation(odorIntensity, materialConcentration);

        return coefficient;
    }

    @Override
    public OdorousMaterialVo odorousMaterialList(Integer page, Integer limit, LocalDateTime start, LocalDateTime end, String siteName) {
        int offset = (page - 1) * limit;
        List<HandleSample> handleSampleList = analysisMapper.getOdorousMaterialList(offset, limit, start, end, siteName);
        Integer total = analysisMapper.getOdorousMaterialNum(start, end, siteName);
        List<OdorousMaterialVo.OdorousMaterial> odorousMaterialList = new ArrayList<>();
        if(handleSampleList != null && !handleSampleList.isEmpty()){
            for (HandleSample handleSample : handleSampleList) {
                OdorousMaterialVo.OdorousMaterial odorousMaterial = new OdorousMaterialVo.OdorousMaterial();
                BeanUtils.copyProperties(handleSample, odorousMaterial);
                String siteCompanyName = handleSample.getSiteName() + "/" + handleSample.getCompanyName();
                odorousMaterial.setSiteCompanyName(siteCompanyName);
                odorousMaterialList.add(odorousMaterial);
            }
        }
        return new OdorousMaterialVo(total, odorousMaterialList);
    }
    @Override
    public List<OdorousMaterialVo.OdorousMaterial> getOdorousMaterialListExcel(LocalDateTime start, LocalDateTime end, Integer page, Integer limit, String siteName) {
        int offset = (page - 1) * limit;
        List<HandleSample> handleSampleList = analysisMapper.getOdorousMaterialList(offset, limit, start, end, siteName);
        Integer total = analysisMapper.getOdorousMaterialNum(start, end, siteName);
        List<OdorousMaterialVo.OdorousMaterial> odorousMaterialList = new ArrayList<>();
        if(handleSampleList != null && !handleSampleList.isEmpty()){
            for (HandleSample handleSample : handleSampleList) {
                OdorousMaterialVo.OdorousMaterial odorousMaterial = new OdorousMaterialVo.OdorousMaterial();
                BeanUtils.copyProperties(handleSample, odorousMaterial);
                String siteCompanyName = handleSample.getSiteName() + "/" + handleSample.getCompanyName();
                odorousMaterial.setSiteCompanyName(siteCompanyName);
                odorousMaterialList.add(odorousMaterial);
            }
        }
        return odorousMaterialList;
    }

    @Override
    public Result<List<SiteComparisonVo>> siteComparisonAnalysis(String startTime, String endTime, String siteName) {
        //将字符串日期转换为 LocalDateTime
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Template.DATETIME_FORMAT);
        LocalDateTime start = LocalDateTime.parse(startTime, formatter);
        LocalDateTime end = LocalDateTime.parse(endTime, formatter);
        //根据时间 点位 获取 点位下样本信息
        List<SiteSampleVo> siteSample = siteMapper.selectSiteComparison(start, end, siteName);
        //用来记录 所有样本id
        List<Integer> sampleIds = new ArrayList<>();
        //辅助记录 样本id
        List<Integer> tempIds = new ArrayList<>();
        //记录 每个点位包含的 样本id
        List<List<Integer>> siteSampleId = new ArrayList<>();
        //最终返回的 结构
        List<SiteComparisonVo> siteComparisonVoList = new ArrayList<>();
        for (SiteSampleVo siteSampleVo : siteSample) {
            List<SampleDetailVo> sampleList = siteSampleVo.getSampleList();
            for (SampleDetailVo sampleDetailVo : sampleList) {
                //将当前点位下 样本id 记录
                sampleIds.add(sampleDetailVo.getSampleId());
                tempIds.add(sampleDetailVo.getSampleId());
            }
            //当前点位下 样本id 记录完毕
            siteSampleId.add(new ArrayList<>(tempIds));
            tempIds.clear();
            SiteComparisonVo siteComparisonVo = new SiteComparisonVo();
            //往最终返回结构中记录 当前点位
            siteComparisonVo.setSiteName(siteSampleVo.getSiteName());
            siteComparisonVoList.add(siteComparisonVo);
        }
        //防止 id集合为空 导致sql 报错
        if(sampleIds.isEmpty()) sampleIds.add(-1);
        //获取样本下的物质
        List<SampleDetailVo> sampleDetailVoList = materialMapper.selectSampleDetail(sampleIds);

        for(int i = 0; i < siteSampleId.size(); ++i) {
            //记录当前点位下 物质类型 防止重复
            Set<String> mtcSet = new HashSet<>();
            //记录当前点位下 物质类型 浓度
            Map<String, SiteComparisonVo.MaterialTypeConcentration> mtcMap = new HashMap<>();
            for(int j = 0; j < siteSampleId.get(i).size(); ++j) {
                for(int k = 0; k < sampleDetailVoList.size(); ++k) {
                    if(siteSampleId.get(i).get(j).compareTo(sampleDetailVoList.get(k).getSampleId()) == 0) {
                        //获取相同样本id 下的 物质列表
                        List<SampleDetailVo.SampleMaterial> sampleMaterialList = sampleDetailVoList.get(k).getSampleMaterialList();
                        //遍历物质列表
                        for(SampleDetailVo.SampleMaterial sampleMaterial : sampleMaterialList) {
                            //提取 物质类型 浓度
                            SiteComparisonVo.MaterialTypeConcentration sm = new SiteComparisonVo.MaterialTypeConcentration();
                            sm.setTypeName(sampleMaterial.getMaterialType());
                            sm.setConcentration(sampleMaterial.getConcentration());
                            sm.setCount(1);
                            //判断此类型是否已经添加
                            if(mtcSet.add(sm.getTypeName())) {
                                //没有被添加 则 添加进Map
                                mtcMap.put(sm.getTypeName(), sm);
                            }else {
                                //已经被添加 则 重新计算浓度
                                SiteComparisonVo.MaterialTypeConcentration sm2 = mtcMap.get(sm.getTypeName());
                                sm2.setCount(sm2.getCount() + 1);
                                sm2.setConcentration((sm2.getConcentration() * (sm2.getCount() - 1) + sm.getConcentration()) / sm2.getCount());
                            }
                        }
                    }
                }
            }
            //将Map 中数据提取出来
            ArrayList<SiteComparisonVo.MaterialTypeConcentration> materialTypeConcentrations = new ArrayList<>(mtcMap.values());
            //获取浓度集合
            List<Double> collect = materialTypeConcentrations.stream().map(SiteComparisonVo.MaterialTypeConcentration::getConcentration).collect(Collectors.toList());
            //计算浓度总和
            siteComparisonVoList.get(i).setTotalConcentration(collect.stream().mapToDouble(Double::doubleValue).sum());
            siteComparisonVoList.get(i).setMaterialTypeConcentrationList(materialTypeConcentrations);
        }
        return Result.ok(siteComparisonVoList);
    }

    @Override
    public Result<ConcentrationContributionVo> concentrationContributions(String startTime, String endTime, String siteName, Integer page, Integer limit) {

        //将字符串日期转换为 LocalDateTime
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Template.DATETIME_FORMAT);
        LocalDateTime start = LocalDateTime.parse(startTime, formatter);
        LocalDateTime end = LocalDateTime.parse(endTime, formatter);

        List<ConcentrationContributionVo.SimpleMaterial> simpleMaterialList = analysisMapper.concentrationContributions((page - 1) * limit, limit, start, end, siteName);
        Integer total = analysisMapper.concentrationContributionsTotal(start, end, siteName);

        ConcentrationContributionVo concentrationContributionVo = new ConcentrationContributionVo(total, simpleMaterialList);

        return Result.ok(concentrationContributionVo);
    }

    @Override
    public Result<List<OdorProfileVo>> odorProfile(String startTime, String endTime) {
        //将字符串日期转换为 LocalDateTime
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Template.DATETIME_FORMAT);
        LocalDateTime start = LocalDateTime.parse(startTime, formatter);
        LocalDateTime end = LocalDateTime.parse(endTime, formatter);

        List<OdorProfileVo> odorProfileVoList = analysisMapper.odorProfile(start, end);
        return Result.ok(odorProfileVoList);
    }

    @Override
    public ClusterAnalysisVo clusterAnalysis(String startTime, String endTime, Integer clusterNum) {
        //将字符串日期转换为 LocalDateTime
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Template.DATETIME_FORMAT);
        LocalDateTime start = LocalDateTime.parse(startTime, formatter);
        LocalDateTime end = LocalDateTime.parse(endTime, formatter);

        ClusterAnalysisVo clusterAnalysisVo = new ClusterAnalysisVo();
        List<SiteMaterialConcentrationBean> siteMaterialConcentrationBeanList = analysisMapper.clusterAnalysis(start, end);
        List<ClusterResultBean> clusterResultBeanList = new ArrayList<>();
        if(!siteMaterialConcentrationBeanList.isEmpty()){
            //遍历每一个点位
            for(SiteMaterialConcentrationBean sMCBean : siteMaterialConcentrationBeanList){
                ClusterResultBean clusterResultBean = new ClusterResultBean();
                clusterResultBean.setSiteName(sMCBean.getSiteName());
                clusterResultBeanList.add(clusterResultBean);
                //获取物质集合
                List<SiteMaterialConcentrationBean.MaterialConcentration> mCList = sMCBean.getMaterialList();
                mCList.removeIf(material -> material.getMaterialName().equals("物质名称"));
                if(!mCList.isEmpty()){
                    double sum = 0.0;
                    for(SiteMaterialConcentrationBean.MaterialConcentration mC : mCList){
                        sum += mC.getConcentration();
                    }
                    if(sum == 0.0){
                        sum = 1.0;
                    }
                    for(SiteMaterialConcentrationBean.MaterialConcentration mC : mCList){
                        mC.setConcentration(mC.getConcentration() / sum);
                    }
                }
            }
            updateAllMaterialList(siteMaterialConcentrationBeanList);
        }
        // 加入点位的浓度数据
        List<double[]> points = new ArrayList<>();
        Map<DoubleArray, String> siteMap = new HashMap<>();
        for(SiteMaterialConcentrationBean sMCBean : siteMaterialConcentrationBeanList){
            List<SiteMaterialConcentrationBean.MaterialConcentration> mCList = sMCBean.getMaterialList();
            if(!mCList.isEmpty()){
                int mCListSize = mCList.size();
                double[] point = new double[mCListSize];
                for(int i = 0; i < mCListSize; i++){
                    point[i] = mCList.get(i).getConcentration();
                }
                DoubleArray doubleArrayKey = new DoubleArray(point);
                siteMap.put(doubleArrayKey, sMCBean.getSiteName());
                points.add(point);
            }
        }
        //kMeans聚类分析
        List<ClusterBean> clusters = kMeans(points, clusterNum);

        int clusterSize = clusters.size();
        for (int i = 0; i < clusterSize; i++) {
            ClusterBean clusterBean = clusters.get(i);
            if(!clusterBean.getPoints().isEmpty()){
                for(double[] point : clusterBean.getPoints()){
                    DoubleArray doubleArrayKey = new DoubleArray(point);
                    String siteName = siteMap.get(doubleArrayKey);
                    for(ClusterResultBean clusterResultBean : clusterResultBeanList){
                        if(clusterResultBean.getSiteName().equals(siteName)){
                            clusterResultBean.setClusterId(i);
                            break;
                        }
                    }
                }
            }
        }
        // 打印结果
        for (ClusterBean cluster : clusters) {
            System.out.println("Cluster with centroid: " + java.util.Arrays.toString(cluster.getCentroid()));
            for (double[] p : cluster.getPoints()) {
                System.out.println("Point concentrations: " + java.util.Arrays.toString(p));
            }
        }
        clusterAnalysisVo.setSiteList(siteMaterialConcentrationBeanList);
        clusterAnalysisVo.setClusterList(clusterResultBeanList);
        return clusterAnalysisVo;
    }

    @Override
    public List<ConcentrationContributionVo.SimpleMaterial> getConcentrationContributionsExcel(String startTime, String endTime, Integer page, Integer limit, String siteName) {
        //将字符串日期转换为 LocalDateTime
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Template.DATETIME_FORMAT);
        LocalDateTime start = LocalDateTime.parse(startTime, formatter);
        LocalDateTime end = LocalDateTime.parse(endTime, formatter);

        List<ConcentrationContributionVo.SimpleMaterial> simpleMaterialList = analysisMapper.concentrationContributions((page - 1) * limit, limit, start, end, siteName);

        return simpleMaterialList;
    }

    private void updateAllMaterialList(List<SiteMaterialConcentrationBean> siteBeanList){
        Set<SiteMaterialConcentrationBean.MaterialConcentration> materialConcentrationSet = new HashSet<>();
        if(!siteBeanList.isEmpty()){
            for(SiteMaterialConcentrationBean site : siteBeanList){
                if(!site.getMaterialList().isEmpty()){
                    materialConcentrationSet.addAll(site.getMaterialList());
                }
            }

            for(SiteMaterialConcentrationBean site : siteBeanList){
                List<SiteMaterialConcentrationBean.MaterialConcentration> materialList = site.getMaterialList();
                Map<String, SiteMaterialConcentrationBean.MaterialConcentration> materialMap = new HashMap<>();
                for(SiteMaterialConcentrationBean.MaterialConcentration material : materialList){
                    materialMap.put(material.getMaterialName(), material);
                }
                // 根据并集更新物质列表
                List<SiteMaterialConcentrationBean.MaterialConcentration> updatedMaterialList = new ArrayList<>();
                for (SiteMaterialConcentrationBean.MaterialConcentration material : materialConcentrationSet) {
                    SiteMaterialConcentrationBean.MaterialConcentration newMaterial = new SiteMaterialConcentrationBean.MaterialConcentration(
                            material.getMaterialId(), material.getMaterialName(), 0.0);
                    updatedMaterialList.add(newMaterial);
                }
                for(SiteMaterialConcentrationBean.MaterialConcentration material : updatedMaterialList){
                    SiteMaterialConcentrationBean.MaterialConcentration materialConcentration = materialMap.get(material.getMaterialName());
                    if(materialConcentration != null){
                        material.setConcentration(materialConcentration.getConcentration());
                    } else{
                        material.setConcentration(0.0);
                    }
                }
                // 设置更新后的物质列表
                site.setMaterialList(updatedMaterialList);
            }
        }
    }

    //kMeans聚类分析
    private List<ClusterBean> kMeans(List<double[]> points, int k) {
        List<ClusterBean> clusters = new ArrayList<>();
        List<double[]> centroids = new ArrayList<>();

        // 初始化 K 个随机质心
        for (int i = 0; i < k; i++) {
            double[] p = points.get(new Random().nextInt(points.size()));
            centroids.add(p.clone());
            clusters.add(new ClusterBean(p.clone()));
        }

        boolean centroidsChanged;
        do {
            clusters.clear();
            for (int i = 0; i < k; i++) {
                clusters.add(new ClusterBean(centroids.get(i).clone()));
            }

            // 将每个点分配给最近的质心
            for (double[] p : points) {
                int closest = -1;
                double closestDistance = Double.MAX_VALUE;
                for (int i = 0; i < k; i++) {
                    double distance = distanceTo(p, centroids.get(i));
                    if (distance < closestDistance) {
                        closestDistance = distance;
                        closest = i;
                    }
                }
                clusters.get(closest).addPoint(p.clone());
            }

            // 更新质心
            centroidsChanged = false;
            for (int i = 0; i < k; i++) {
                ClusterBean cluster = clusters.get(i);
                cluster.calculateCentroid();
                for (int j = 0; j < centroids.get(i).length; j++) {
                    double oldCentroidValue = centroids.get(i)[j];
                    double newCentroidValue = cluster.getCentroid()[j];
                    if (Math.abs(oldCentroidValue - newCentroidValue) > 1e-10) {
                        centroidsChanged = true;
                    }
                    centroids.set(i, cluster.getCentroid().clone());
                }
            }
        } while (centroidsChanged);

        return clusters;
    }

    public static double distanceTo(double[] p1, double[] p2) {
        double sum = 0.0;
        for (int i = 0; i < p1.length; i++) {
            sum += (p1[i] - p2[i]) * (p1[i] - p2[i]);
        }
        return Math.sqrt(sum);
    }

}
